package xyz.xtt.equity.service.impl;

import static xyz.xtt.common.asset.AssertUtils.isFalse;
import static xyz.xtt.common.asset.AssertUtils.isNull;
import static xyz.xtt.common.asset.AssertUtils.isTrue;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.request.ActivityTypeEnum;
import xyz.xtt.asset.client.request.DeliverRequest;
import xyz.xtt.asset.client.request.ExecDeliverResult;
import xyz.xtt.asset.client.response.AssetAccountResponse;
import xyz.xtt.equity.client.request.SendDeliverResultRequest;
import xyz.xtt.equity.constants.DeliverResult;
import xyz.xtt.equity.constants.EquityTypeEnum;
import xyz.xtt.equity.constants.ErrorMessage;
import xyz.xtt.equity.entity.TblAssetEntitlement;
import xyz.xtt.equity.entity.TblAssetEntitlementDeliver;
import xyz.xtt.equity.mapper.TblAssetEntitlementDeliverMapper;
import xyz.xtt.equity.mapper.TblAssetEntitlementMapper;
import xyz.xtt.equity.service.IInnerService;
import xyz.xtt.equity.service.ITblAssetEntitlementDeliverService;
import xyz.xtt.user.client.response.UserInfoResponse;

/**
 * <p>
 * 资产权益兑换表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-12-27
 */
@Slf4j
@Service
public class TblAssetEntitlementDeliverServiceImpl extends ServiceImpl<TblAssetEntitlementDeliverMapper, TblAssetEntitlementDeliver>
		implements ITblAssetEntitlementDeliverService {
	private static final String GENDER_PARAM = "gender";
	@Autowired
	private IInnerService iInnerService;
	@Autowired
	private TblAssetEntitlementMapper tblAssetEntitlementMapper;
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Override
	public TblAssetEntitlementDeliver info(Long entitlementId) {
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		return query.eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId).one();
	}

	@Override
	public boolean saveOrUpdateByDeliver(TblAssetEntitlementDeliver deliver, boolean retry) {
		if (retry) {
			LambdaUpdateChainWrapper<TblAssetEntitlementDeliver> update = new LambdaUpdateChainWrapper<>(this.baseMapper);
			update
					.eq(TblAssetEntitlementDeliver::getAssetId, deliver.getAssetId())
					.eq(TblAssetEntitlementDeliver::getAppUserId, deliver.getAppUserId())
					.eq(TblAssetEntitlementDeliver::getEntitlementId, deliver.getEntitlementId())
					.set(TblAssetEntitlementDeliver::getResult, deliver.getResult())
					.set(TblAssetEntitlementDeliver::getResponeText, deliver.getResponeText());
			return update.update();
		}
		return this.save(deliver);
	}

	@Override
	public TblAssetEntitlementDeliver deliver(Long assetId, Long entitlementId, Long uid) {
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		return query
				.eq(TblAssetEntitlementDeliver::getAssetId, assetId)
				.eq(TblAssetEntitlementDeliver::getAppUserId, uid)
				.eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId)
				.one();
	}

	@Override
	public TblAssetEntitlementDeliver deliver(Long entitlementId, Long uid) {
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		return query.eq(TblAssetEntitlementDeliver::getAppUserId, uid).eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId).one();
	}

	@Override
	public List<Long> getAssetEntitlementDeliver(Long assetId, Long userId) {
		// 查询权益兑换详情
		MPJLambdaWrapper<TblAssetEntitlementDeliver> query = JoinWrappers
				.lambda(TblAssetEntitlementDeliver.class)
				.select(TblAssetEntitlementDeliver::getEntitlementId)
				.eq(TblAssetEntitlementDeliver::getAssetId, assetId)
				.eq(TblAssetEntitlementDeliver::getResult, DeliverResult.SUCC.code())
				.eq(TblAssetEntitlementDeliver::getAppUserId, userId);
		return query.list(Long.class);
	}

	@Override
	public boolean checkDeliverSucc(Long entitlementId, Long uid) {
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		return query
				.eq(TblAssetEntitlementDeliver::getAppUserId, uid)
				.eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId)
				.eq(TblAssetEntitlementDeliver::getResult, DeliverResult.SUCC.code())
				.count() > 0;
	};

	private ExecDeliverResult result(TblAssetEntitlementDeliver deliver) {
		boolean noDeliver = Objects.isNull(deliver);
		if (noDeliver) {
			return ExecDeliverResult.NO;
		}
		boolean r = deliver.isDeliver();
		if (r) {
			return ExecDeliverResult.YES;
		} else {
			return ExecDeliverResult.FAIL;
		}
	}

	@Override
	public Boolean exchangeWorkEx(Long uid, Long id, Integer gender, Long assetId) {
		UserInfoResponse userInfo = iInnerService.userInfo(uid);
		isNull(userInfo, ErrorMessage.USER_NOT_EXIST);
		TblAssetEntitlement tblAssetEntitlement = tblAssetEntitlementMapper.selectById(id);
		isNull(tblAssetEntitlement, ErrorMessage.ENTITLEMENT_NOT_EXIST);
		isFalse(tblAssetEntitlement.checkTime(), ErrorMessage.INVALID_TIME_RANGE);
		isTrue(tblAssetEntitlement.checkAmount(), ErrorMessage.EQUITYS_NUMBER_ZERO);
		TblAssetEntitlementDeliver deliver = deliver(id, uid);
		ExecDeliverResult beforeResult = result(deliver);

//		boolean checkDeliverSucc = checkDeliverSucc(id, uid);
		isTrue(Objects
				.equals(ExecDeliverResult.YES, beforeResult), ErrorMessage.ENTITLEMENT_ALREADY_REDEEMED, () -> log.info("uid:{},entitlementId:{}", uid, id));
		String key = String.format("equity_exchange:%s:%s", uid, id);
		String val = UUID.randomUUID().toString();
		boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(key, val, 1, TimeUnit.MINUTES);
		if (lock) {
			try {
				AssetAccountResponse account = iInnerService.accountInfoUnCheckAmount(assetId, uid);
				isTrue(Objects.nonNull(account) && BigDecimal.ZERO.compareTo(account.getAmount()) == 0, ErrorMessage.EQUITY_EXCHANGE_USER_WRONG);
				EquityTypeEnum requestType = EquityTypeEnum.requestType(tblAssetEntitlement.getEquityType());
				isNull(requestType, ErrorMessage.ENTITLEMENT_NOT_EXIST, () -> log.info("convert requestType is null {}", tblAssetEntitlement.getEquityType()));
				DeliverRequest request = DeliverRequest
						.builder()
						.assetId(assetId)
						.activityType(requestType.thirdType())
						.targetUid(uid)
						.entitlementId(id)
						.beforeResult(beforeResult)
						.beforeReqponse(deliver.getResponeText())
						.build();
				if (requestType.checkThirdRequest()) {
					buildSuitGender(request, gender, userInfo);
					log.info("user uid:{}, request param: {}", userInfo.getAppUserId(), JSONUtil.toJsonStr(request));
					Boolean response = iInnerService.deliver(request);
					return response;
				} else {
					return saveLocalDeliver(uid, id, assetId);
				}
			} catch (Exception e) {
				log.info("调用兑换接口异常，异常信息为: {}", e.getMessage(), e);
			} finally {
				if (val.equals(stringRedisTemplate.opsForValue().get(key))) {
					stringRedisTemplate.delete(key);
				}
			}
		}
		return Boolean.FALSE;
	}

	private Boolean saveLocalDeliver(Long uid, Long entitlementId, Long assetId) {
		TblAssetEntitlementDeliver de = new TblAssetEntitlementDeliver();
		de.setResult(DeliverResult.SUCC.code());
		de.setAppUserId(uid);
		de.setAssetId(assetId);
		de.setEntitlementId(entitlementId);
		de.setId(IdUtil.getSnowflakeNextId());
		de.setCreateTime(System.currentTimeMillis());
		return baseMapper.insert(de) == 1;
	}

	private static void buildSuitGender(DeliverRequest request, Integer gender, UserInfoResponse userInfo) {
		// 套装和勋章，分男女
		boolean checkType = Objects.equals(request.getActivityType(), EquityTypeEnum.EQUITY_TYPE_02.thirdType())
				|| Objects.equals(request.getActivityType(), EquityTypeEnum.EQUITY_TYPE_07.thirdType());
		if (checkType) {
			Integer userGender = Optional.ofNullable(userInfo).filter(UserInfoResponse::filterGender).map(UserInfoResponse::getGender).orElse(gender);
			if (Objects.equals(userGender, 1) || Objects.equals(userGender, 2)) {
				if (Objects.equals(request.getActivityType(), EquityTypeEnum.EQUITY_TYPE_02.thirdType())) {
					request.setActivityType(ActivityTypeEnum.SUIT_GENDER.name());
				} else if (Objects.equals(request.getActivityType(), EquityTypeEnum.EQUITY_TYPE_07.thirdType())) {
					request.setActivityType(ActivityTypeEnum.INSIGNE.name());
				}
				request.setExternParm(JSONUtil.toJsonStr(Map.of(GENDER_PARAM, userGender)));
			}
		}
	}

	@Override
	public Boolean deliverCheck(Long entitlementId, Long uid) {
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		Long count = query.eq(TblAssetEntitlementDeliver::getAppUserId, uid).eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId).count();
		return Objects.nonNull(count) && count.longValue() > 0;
	}

	@Override
	public TblAssetEntitlementDeliver info(Long entitlementId, String uid) {
		Long lUid = Long.parseLong(uid);
		LambdaQueryChainWrapper<TblAssetEntitlementDeliver> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		return query.eq(TblAssetEntitlementDeliver::getEntitlementId, entitlementId).eq(TblAssetEntitlementDeliver::getAppUserId, lUid).one();

	}

	@Override
	public Boolean saveDeliverResult(SendDeliverResultRequest request) {
		TblAssetEntitlementDeliver buildDeliver = buildDeliver(request.getAppUserId(), request.getAssetId(), request.getEntitlementId(), request
				.getResponeText(), request.getResult());
		boolean saveOrUpdate = saveOrUpdate(buildDeliver);
		return saveOrUpdate;
	}

	public TblAssetEntitlementDeliver buildDeliver(Long appUserId, Long assetId, Long entitlementId, String responeText, Integer result) {
		return TblAssetEntitlementDeliver
				.builder()
				.id(IdUtil.getSnowflakeNextId())
				.assetId(assetId)
				.entitlementId(entitlementId)
				.appUserId(appUserId)
				.createTime(System.currentTimeMillis())
				.responeText(responeText)
				.result(result)
				.build();
	}
}
